# --*--utf8--*--
from tkinter import *
from tkinter.ttk import *
import tkinter.messagebox as messageBox
import tkinter.tix
from ctypes import *
import os, sys
import collections
import time
from concurrent.futures import ThreadPoolExecutor
import threading
from abc import abstractmethod
import json
import pysnooper
from codescanner import CodeScanner

from funcbase import UnitTestDataStructure
from functest import *
from loggers import dev_logger, prod_logger

# 定义接收信息的具名元组结构
RecvMsg = collections.namedtuple("RecvMsg", ["id", "data"])
ModelPos = collections.namedtuple("ModelPos", ["row", "column"])

if 1:
# with pysnooper.snoop():
    """
    全局变量
    """
    DevType = c_uint

    """
        Device Type
    """
    USBCAN1 = DevType(3)
    USBCAN2 = DevType(4)
    USBCANFD = DevType(6)
    """
        Device Index
    """
    DevIndex = c_uint(0)  # 设备索引
    """
        Channel
    """
    Channel1 = c_uint(0)  # CAN1
    Channel2 = c_uint(1)  # CAN2
    """
        ECAN Status
    """
    STATUS_ERR = 0
    STATUS_OK = 1

    """
        Device Information
    """


if 1:
    """
    can相关数据结构
    """

    class BoardInfo(Structure):
        _fields_ = [
            ("hw_Version", c_ushort),  # 硬件版本号，用16进制表示
            ("fw_Version", c_ushort),  # 固件版本号，用16进制表示
            ("dr_Version", c_ushort),  # 驱动程序版本号，用16进制表示
            ("in_Version", c_ushort),  # 接口库版本号，用16进制表示
            ("irq_Num", c_ushort),  # 板卡所使用的中断号
            ("can_Num", c_byte),  # 表示有几路CAN通道
            ("str_Serial_Num", c_byte * 20),  # 此板卡的序列号，用ASC码表示
            ("str_hw_Type", c_byte * 40),  # 硬件类型，用ASC码表示
            ("Reserved", c_byte * 4),
        ]  # 系统保留

    class CAN_OBJ(Structure):
        _fields_ = [
            ("ID", c_uint),  # 报文帧ID
            ("TimeStamp", c_uint),  # 接收到信息帧时的时间标识，从CAN控制器初始化开始计时，单位微秒
            (
                "TimeFlag",
                c_byte,
            ),  # 是否使用时间标识，为1时TimeStamp有效，TimeFlag和TimeStamp只在此帧为接收帧时有意义。
            ("SendType", c_byte),
            # 发送帧类型。=0时为正常发送，=1时为单次发送（不自动重发），=2时为自发自收（用于测试CAN卡是否损坏），=3时为单次自发自收（只发送一次，用于自测试），只在此帧为发送帧时有意义
            ("RemoteFlag", c_byte),  # 是否是远程帧。=0时为数据帧，=1时为远程帧
            ("ExternFlag", c_byte),  # 是否是扩展帧。=0时为标准帧（11位帧ID），=1时为扩展帧（29位帧ID）
            ("DataLen", c_byte),  # 数据长度DLC(<=8)，即Data的长度
            ("data", c_ubyte * 8),  # CAN报文的数据。空间受DataLen的约束
            ("Reserved", c_byte * 3),
        ]  # 系统保留。

    class INIT_CONFIG(Structure):
        _fields_ = [
            ("acccode", c_uint32),  # 验收码。SJA1000的帧过滤验收码
            ("accmask", c_uint32),  # 屏蔽码。SJA1000的帧过滤屏蔽码。屏蔽码推荐设置为0xFFFF FFFF，即全部接收
            ("reserved", c_uint32),  # 保留
            ("filter", c_byte),  # 滤波使能。0=不使能，1=使能。使能时，请参照SJA1000验收滤波器设置验收码和屏蔽码
            ("timing0", c_byte),  # 波特率定时器0,详见动态库使用说明书7页
            ("timing1", c_byte),  # 波特率定时器1,详见动态库使用说明书7页
            ("mode", c_byte),
        ]  # 模式。=0为正常模式，=1为只听模式，=2为自发自收模式。

    class ECAN(object):
        def __init__(self):
            cwdx = os.path.join(
                os.path.dirname(os.path.abspath((__file__))), "eCanVci64.dll"
            )
            self.dll = cdll.LoadLibrary(cwdx)
            if self.dll == None:
                prod_logger.error("DLL Couldn't be loaded")

        def OpenDevice(self, DeviceType, DeviceIndex):
            try:
                return self.dll.OpenDevice(DeviceType, DeviceIndex, 0)
            except:
                prod_logger.error("Exception on OpenDevice!")
                raise

        def CloseDevice(self, DeviceType, DeviceIndex):
            try:
                return self.dll.CloseDevice(DeviceType, DeviceIndex, 0)
            except:
                prod_logger.error("Exception on CloseDevice!")
                raise

        def InitCan(self, DeviceType, DeviceIndex, CanInd, Initconfig):
            try:
                return self.dll.InitCAN(
                    DeviceType, DeviceIndex, CanInd, byref(Initconfig)
                )
            except:
                prod_logger.error("Exception on InitCan!")
                raise

        def StartCan(self, DeviceType, DeviceIndex, CanInd):
            try:
                return self.dll.StartCAN(DeviceType, DeviceIndex, CanInd)
            except:
                prod_logger.error("Exception on StartCan!")
                raise

        def ReadBoardInfo(self, DeviceType, DeviceIndex):
            try:
                mboardinfo = BoardInfo()
                ret = self.dll.ReadBoardInfo(DeviceType, DeviceIndex, byref(mboardinfo))
                return mboardinfo, ret
            except:
                prod_logger.error("Exception on ReadBoardInfo!")
                raise

        def Receivce(self, DeviceType, DeviceIndex, CanInd, length):
            try:
                recmess = (CAN_OBJ * length)()
                ret = self.dll.Receive(
                    DeviceType, DeviceIndex, CanInd, byref(recmess), length, 0
                )
                return length, recmess, ret
            except:
                prod_logger.error("Exception on Receive!")
                raise

        def Tramsmit(self, DeviceType, DeviceIndex, CanInd, mcanobj):
            try:
                # mCAN_OBJ=CAN_OBJ*2
                # self.dll.Transmit.argtypes = [ctypes.c_uint32, ctypes.c_uint32, ctypes.c_uint32, POINTER(CAN_OBJ),
                # ctypes.c_uint16]
                return self.dll.Transmit(
                    DeviceType, DeviceIndex, CanInd, byref(mcanobj), c_uint16(1)
                )
            except:
                prod_logger.error("Exception on Tramsmit!")
                raise
        
        # 获取指定接收缓存区中接收到，但尚未被读取的帧数量
        def GetReceiveNum(self,DeviceType, DeviceIndex, CanInd ):
            try:
                # 返回尚未被读取的帧数
                bufferCounts = self.dll.GetReceiveNum(DeviceType, DeviceIndex, CanInd)
                dev_logger.info(f'当前Buffer中缓存的帧数为{bufferCounts}条')
                return bufferCounts
            except:
                prod_logger.error('Exception on GetReceiveNum !')
                raise
        


        # 增加clearBuffer功能
        def ClearBuffer(self, DeviceType, DeviceIndex, CanInd):

            # 先查看缓冲区有多少条数据？
            self.GetReceiveNum(DeviceType, DeviceIndex, CanInd)
            
            try:
                # 返回1表示操作成功，返回0表示操作失败
                ret = self.dll.ClearBuffer(DeviceType, DeviceIndex, CanInd)
                self.GetReceiveNum(DeviceType, DeviceIndex, CanInd)
                return ret
            except:
                prod_logger.error('Exception on ClearBuffer!')
                raise


if 1:
    # 加载动态库
    if hasattr(sys, "frozen"):
        os.environ["PATH"] = sys._MEIPASS + ":" + os.environ["PATH"]
    """
    读取数据
    """


def getTiming(mbaud):
    if mbaud == "1M":
        return 0, 0x14
    if mbaud == "800k":
        return 0, 0x16
    if mbaud == "666k":
        return 0x80, 0xB6
    if mbaud == "500k":
        return 0, 0x1C
    if mbaud == "400k":
        return 0x80, 0xFA
    if mbaud == "250k":
        return 0x01, 0x1C
    if mbaud == "200k":
        return 0x81, 0xFA
    if mbaud == "125k":
        return 0x03, 0x1C
    if mbaud == "100k":
        return 0x04, 0x1C
    if mbaud == "80k":
        return 0x83, 0xFF
    if mbaud == "50k":
        return 0x09, 0x1C


# 定义抽象基类做基本设置把需要单独设置的放到业务类里
#
class GcanViewBase(Tk):

    # 设定位置ID清单
    MODELIDLIST = []
    MAX_ROW = 8
    MAX_COLUMN = 8
    for i in range(MAX_ROW):
        for j in range(MAX_COLUMN):
            position = f"{i}{j}"
            MODELIDLIST.append(position)

    testmodelDict = {}
    testfuncDict = {}
    for i, j in MODELIDLIST:
        # TESTMODELLIST[f"{i}{j}"] = f"模块-R{i}C{j}"
        testmodelDict[f"{i}{j}"] = TestDataStucture(PosId=f"{i}{j}")
        testfuncDict[f"{i}{j}"] = (
            lambda i, j: lambda: tkinter.messagebox.showinfo("提示", f"R{i}C{j}未定义!")
        )(i, j)

    TESTCONFIGDATALIST = []

    def __init__(self):
        super().__init__()
        self.ecan = ECAN()
        self.musbcanopen = False
        self.rec_CAN1 = 1
        self.rec_CAN2 = 1
        self.manualReadCanCounts = 0
        # 创建进程池
        self.pool = ThreadPoolExecutor(max_workers=5)
        # 持续读取总线数据
        self.pool.submit(self.ReadCAN)
        self.tkViewInit()
        # TODO: 最大长度需要合理设定
        self.recvMsgDeque = collections.deque(maxlen=200)
        self.recvMsgDequeLock = threading.Lock()

    def tkViewInit(self):
        self.title("Gcan view (refactor by angelyhch)")
        self.resizable(width=True, height=True)
        self.tk.eval("package require Tix")
        self.deviceConfigFrameInit()
        self.tabControlInit()

        # 新建用户窗口
        # 后续可以根据具体的测试需求更新展示内容
        self.userWindowInit()
        # 输入框获取焦点
        # FIXME :如果通过扫码获取焦点,就不需要一开始获取焦点,在获取到数据后再设定获取焦点
        # self.curVinCodeEntry.focus_set()
    
    def userWindowInit(self):

        
        # FIXME: 此处用Toplevel，不能直接用Tk生成实例，否则会报错main thread is not in mainloop
        self.userWindow = Toplevel(self)
        self.userWindow.geometry('900x600+600+200')
        self.userWindow.title('座椅测试操作台')
        self.scanner = CodeScanner()

        # 初始化can盒
        self.caninit()
        

        # 隐藏主窗口
        self.withdraw()


        # 输入Vin码边框
        self.userWindowFrame1 = Frame(self.userWindow, relief='groove')
        self.userWindowFrame1.grid(row=1, column=1, padx=20, pady=20)
        if 1:

            # 该组件使用的回调函数
            if 1:
                # TODO: 后期改为扫码获取值,与OBD读取值得比对
                def validateVinCode():
                    # Vin码输入框验证函数
                    curVin = self.curVinCode.get()
                    # print(f'validate VinCode {curVin}')

                    # TODO: 待开启VIN匹配功能
                    pat = re.compile(r'^LNB\w{8}\d{6}$')
                    if pat.match(curVin):
                    # if len(curVin) > 4:

                        self.userWindowFrame2_1Btn1.configure(state='normal')
                        self.userWindowFrame2_1Btn2.configure(state='normal')
                        self.userWindowFrame2_1Btn3.configure(state='normal')
                        self.userWindowFrame2_1Btn4.configure(state='normal')
                        return True
                        
                    else:
                        return False

                def validateVinCodeFailed():
                    self.userWindowFrame2_1Btn1.configure(state='disabled')
                    self.userWindowFrame2_1Btn2.configure(state='disabled')
                    self.userWindowFrame2_1Btn3.configure(state='disabled')
                    self.userWindowFrame2_1Btn4.configure(state='disabled')
                    messageBox.showwarning('VIN码验证警告','VIN码验证不通过，请核对输入的VIN码是否正确？')
                    # print('validate failed...')
                    pass

                def curVinCodeEntryReset():
                # 清理输入框内容，获取焦点
                    self.curVinCodeEntry.delete(0,END)
                    self.curVinCodeEntry.focus_set()
                    
                    # 设置按钮使能
                    # TODO: 后期将几个测试按钮的使能操作打包起来,或者用容器装起来,遍历实现可扩展性.
                    self.userWindowFrame2_1Btn1.configure(state='disabled')
                    self.userWindowFrame2_1Btn2.configure(state='disabled')
                    self.userWindowFrame2_1Btn3.configure(state='disabled')
                    self.userWindowFrame2_1Btn4.configure(state='disabled')

                # 通过扫码获取vin,然后把输入框获取焦点,用于validate
                def curVinCodeScanStart():
                    resultCode = self.scanner.scan()
                    self.curVinCode.set(resultCode)
                    self.curVinCodeEntry.focus_set()
                    pass

            self.curVinCodeLabel = Label(self.userWindowFrame1, text='输入当前VIN码')
            self.curVinCodeLabel.grid(row=1, column=1, padx=10, pady=10)
            self.curVinCode = StringVar()
            self.curVinCodeEntry = Entry(self.userWindowFrame1, textvariable=self.curVinCode,validate='focusout', validatecommand=validateVinCode, invalidcommand=validateVinCodeFailed)
            self.curVinCodeEntry.grid(row=1, column=2, padx=10, pady=10)
            self.curVinCodeConfirmBtn = Button(self.userWindowFrame1, text='确认')
            self.curVinCodeConfirmBtn.grid(row=1, column=3, padx=10, pady=10)
            self.curVinCodeResetBtn = Button(self.userWindowFrame1, text='重置', command=curVinCodeEntryReset)
            self.curVinCodeResetBtn.grid(row=1, column=4, padx=10, pady=10)
            self.curVinCodeScanBtn = Button(self.userWindowFrame1, text='开始扫码', command=curVinCodeScanStart)
            self.curVinCodeScanBtn.grid(row=2,column=1, columnspan=2, padx=10, pady=10, sticky=EW)


        self.userWindowFrame2 = Frame(self.userWindow)
        self.userWindowFrame2.grid(row=2, column=1, padx=20, pady=20)
        if 1:
            self.userWindowFrame2_1 = Frame(self.userWindowFrame2, relief='groove')
            self.userWindowFrame2_1.grid(row=1, column=1)
            # TODO: 后期改成遍历循环模式
            if 1:
                self.userWindowFrame2_1TestResMsg ={}
                self.userWindowFrame2_1Btn1 = Button(self.userWindowFrame2_1,state=DISABLED,text='主驾座椅批量测试',command=self.unitTestCommand('00'))
                self.userWindowFrame2_1Btn1.grid(row=1,column=1,padx=10,pady=10)
                self.userWindowFrame2_1TestResMsg['00'] = Message(self.userWindowFrame2_1,width=200,foreground='gray',text='主驾批量测试结果')
                self.userWindowFrame2_1TestResMsg['00'].grid(row=1, column=2,padx=10, pady=10)
                self.userWindowFrame2_1Btn2 = Button(self.userWindowFrame2_1,state=DISABLED,text='副驾座椅批量测试',command=self.unitTestCommand('01'))
                self.userWindowFrame2_1Btn2.grid(row=1,column=3,padx=10, pady=10)
                self.userWindowFrame2_1TestResMsg['01'] = Message(self.userWindowFrame2_1,width=200,foreground='gray',text='副驾批量测试结果')
                self.userWindowFrame2_1TestResMsg['01'].grid(row=1, column=4,padx=10, pady=10)
                self.userWindowFrame2_1Btn3 = Button(self.userWindowFrame2_1,state=DISABLED,text='左后座椅批量测试',command=self.unitTestCommand('02'))
                self.userWindowFrame2_1Btn3.grid(row=2,column=1,padx=10,pady=10)
                self.userWindowFrame2_1TestResMsg['02'] = Message(self.userWindowFrame2_1,width=200,foreground='gray',text='左后批量测试结果')
                self.userWindowFrame2_1TestResMsg['02'].grid(row=2, column=2,padx=10, pady=10)
                self.userWindowFrame2_1Btn4 = Button(self.userWindowFrame2_1,state=DISABLED,text='右后座椅批量测试',command=self.unitTestCommand('03'))
                self.userWindowFrame2_1Btn4.grid(row=2,column=3,padx=10, pady=10)
                self.userWindowFrame2_1TestResMsg['03'] = Message(self.userWindowFrame2_1,width=200,foreground='gray',text='右后批量测试结果')
                self.userWindowFrame2_1TestResMsg['03'].grid(row=2, column=4,padx=10, pady=10)
        # FIXME: 此处不需要mainloop

        # 测试结果显示框
        self.userWindowFrame6 = Frame(self.userWindow)
        self.userWindowFrame6.grid(row=6,column=1, padx=20,pady=20)
        if 1:

            self.userWindowFrame6recordListboxScroll = Scrollbar(self.userWindowFrame6, orient=VERTICAL)
            self.userWindowFrame6recordListboxScroll.grid(row=2, column=11, sticky="ns")
            self.userWindowFrame6recordListbox = Listbox(
                self.userWindowFrame6,
                font=("Arial", 12),
                height=10,
                width=80,
                yscrollcommand=self.userWindowFrame6recordListboxScroll.set,
            )
            self.userWindowFrame6recordListbox.grid(row=2, column=0, columnspan=11, sticky="nw")
            self.userWindowFrame6recordListboxScroll.config(command=self.userWindowFrame6recordListbox.yview)
            self.userWindowFrame6recordClearBtn = Button(
                self.userWindowFrame6,
                text="清空测试记录",
                command=lambda: self.userWindowFrame6recordListbox.delete(0,END),
            )
            self.userWindowFrame6recordClearBtn.grid(row=2, column=12, padx=2, pady=1)
            pass

            
        # 显示主窗口按钮
        self.userWindowFrame8 = Frame(self.userWindow)
        self.userWindowFrame8.grid(row=8, column=1, padx=20, pady=20)
        if 1:
            self.userWindowFrame8Btn1 = Button(self.userWindowFrame8,text='显示主窗口',command=self.deiconify)
            self.userWindowFrame8Btn1.grid(row=1, column=1, padx=10, pady=10)


    def deviceConfigFrameInit(self):
        self.deviceConfigFrame = Frame(self)
        self.deviceConfigFrame.grid(row=0, column=0)
        self.lb1 = Label(
            self.deviceConfigFrame, text="CAN1波特率:", borderwidth=3, font=("Arial", 12)
        )
        self.lb1.grid(row=1, column=0, padx=1, pady=1, sticky="w")
        self.lb2 = Label(
            self.deviceConfigFrame, text="CAN2波特率:", borderwidth=3, font=("Arial", 12)
        )
        self.lb2.grid(row=2, column=0, padx=1, pady=1, sticky="w")
        self.lbsn = Label(
            self.deviceConfigFrame,
            text="SN:",
            borderwidth=3,
            font=("Arial", 12),
            width=30,
        )
        self.lbsn.grid(row=2, column=3, padx=5, pady=5, sticky="w")

        self.baudvaluecan1 = StringVar()
        self.baudvaluecan1.set("500k")
        self.baudvaluecan2 = StringVar()
        self.baudvaluecan2.set("500k")
        self.baudvalues = [
            "1M",
            "800k",
            "666k",
            "500k",
            "400k",
            "250k",
            "200k",
            "125k",
            "100k",
            "80k",
            "50k",
        ]
        self.can1com = Combobox(
            master=self.deviceConfigFrame,
            state="readonly",
            font=("Arial", 12),
            textvariable=self.baudvaluecan1,
            values=self.baudvalues,
        )
        self.can1com.grid(row=1, column=1, padx=1, pady=1, sticky="nw")
        self.can2com = Combobox(
            master=self.deviceConfigFrame,
            state="readonly",
            font=("Arial", 12),
            textvariable=self.baudvaluecan2,
            values=self.baudvalues,
        )
        self.can2com.grid(row=2, column=1, padx=1, pady=1, sticky="w")
        self.btopen = Button(self.deviceConfigFrame, text="打开设备", command=self.caninit)
        self.btopen.grid(row=1, column=2, padx=1, pady=1, sticky="w")
        self.btreadinfo = Button(
            self.deviceConfigFrame,
            text="读取设备信息",
            command=self.readmess,
            state="disabled",
        )
        self.btreadinfo.grid(row=2, column=2, padx=1, pady=1, sticky="w")

        # 隐藏主窗口按钮
        self.hideMainWindow =Button( self.deviceConfigFrame, text='隐藏主窗口', command=self.iconify )
        self.hideMainWindow.grid(row=2, column=10, padx=10, pady=10)

    def tabControlInit(self):
        self.tabcontrol = Notebook(self)
        self.tabcontrol.grid(row=3, columnspan=5, sticky="nw")
        self.tab1 = Frame(self.tabcontrol)
        self.tab2 = Frame(self.tabcontrol)
        self.tab5 = Frame(self.tabcontrol)
        self.tab6 = Frame(self.tabcontrol)
        self.tabcontrol.add(self.tab1, text="CAN1")
        self.tabcontrol.add(self.tab2, text="CAN2")
        self.tabcontrol.add(self.tab5, text="单元测试库")
        # self.tabcontrol.add(self.tab6,text="座椅测试面板")

        self.can1TabInit()
        self.can2TabInit()
        self.tab5InitTestPanel()
        # self.tab6InitTabSeatPanel()
        

    def can1TabInit(self):

        # 发送数据界面
        self.sendMessage1FrameInit()

        # 接收数据界面
        self.recvMessage1FrameInit()

    def sendMessage1FrameInit(self):
        self.lb_ID_CAN1 = Label(
            self.tab1, text="ID(Hex)", borderwidth=3, font=("Arial", 12)
        )
        self.lb_ID_CAN1.grid(row=0, column=0, sticky="w")
        self.e_ID_CAN1 = Entry(self.tab1, font=("Arial", 12))
        self.e_ID_CAN1.grid(row=1, column=0, sticky="w")
        self.e_ID_CAN1.insert(0, "5d3")
        self.ext_CAN1 = IntVar()
        self.cb_Ext_CAN1 = Checkbutton(
            self.tab1,
            text="Extended",
            variable=self.ext_CAN1,
            # TODO 字体设置
            # font=("Arial", 12),
        )
        self.cb_Ext_CAN1.grid(row=0, column=1, sticky="w")
        self.rtr_CAN1 = IntVar()
        self.cb_Rtr_CAN1 = Checkbutton(
            self.tab1,
            text="RTR",
            variable=self.rtr_CAN1,
            # TODO:
            # borderwidth=3,
            # font=("Arial", 12),
        )
        self.cb_Rtr_CAN1.grid(row=1, column=1, sticky="w")
        self.ct_Length_CAN1 = tkinter.tix.Control(
            self.tab1, label="Length(0-8):", integer=True, max=8, min=0, value=8, step=1
        )
        self.ct_Length_CAN1.grid(row=0, column=7, columnspan=4, sticky="w")
        self.lb_Data_CAN1 = Label(
            self.tab1, text="Data(Hex)", borderwidth=3, font=("Arial", 12)
        )
        self.lb_Data_CAN1.grid(row=0, column=3, columnspan=4, sticky="w")
        self.Data0_CAN1 = StringVar()
        self.e_Data0_CAN1 = Entry(
            self.tab1,
            textvariable=self.Data0_CAN1,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data0_CAN1.grid(row=1, column=3, padx=2, pady=1, sticky="w")
        self.Data0_CAN1.set("00")
        self.Data1_CAN1 = StringVar()
        self.e_Data1_CAN1 = Entry(
            self.tab1,
            textvariable=self.Data1_CAN1,
            width=3,
            # borderwidth=3,
            font=("Arial", 12),
        )
        self.e_Data1_CAN1.grid(row=1, column=4, padx=2, pady=1, sticky="w")
        self.Data1_CAN1.set("00")
        self.Data2_CAN1 = StringVar()
        self.e_Data2_CAN1 = Entry(
            self.tab1,
            textvariable=self.Data2_CAN1,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data2_CAN1.grid(row=1, column=5, padx=2, pady=1, sticky="w")
        self.Data2_CAN1.set("00")
        self.Data3_CAN1 = StringVar()
        self.e_Data3_CAN1 = Entry(
            self.tab1,
            textvariable=self.Data3_CAN1,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data3_CAN1.grid(row=1, column=6, padx=2, pady=1, sticky="w")
        self.Data3_CAN1.set("00")
        self.Data4_CAN1 = StringVar()
        self.e_Data4_CAN1 = Entry(
            self.tab1,
            textvariable=self.Data4_CAN1,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data4_CAN1.grid(row=1, column=7, padx=2, pady=1, sticky="w")
        self.Data4_CAN1.set("12")
        self.Data5_CAN1 = StringVar()
        self.e_Data5_CAN1 = Entry(
            self.tab1,
            textvariable=self.Data5_CAN1,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data5_CAN1.grid(row=1, column=8, padx=2, pady=1, sticky="w")
        self.Data5_CAN1.set("00")
        self.Data6_CAN1 = StringVar()
        self.e_Data6_CAN1 = Entry(
            self.tab1,
            textvariable=self.Data6_CAN1,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data6_CAN1.grid(row=1, column=9, padx=2, pady=1, sticky="w")
        self.Data6_CAN1.set("00")
        self.Data7_CAN1 = StringVar()
        self.e_Data7_CAN1 = Entry(
            self.tab1,
            textvariable=self.Data7_CAN1,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data7_CAN1.grid(row=1, column=10, padx=2, pady=1, sticky="w")
        self.Data7_CAN1.set("00")
        self.bt_send_CAN1 = Button(
            self.tab1,
            text="发送数据",
            state="disabled",
            command=self.sendcan1,
        )
        self.bt_send_CAN1.grid(row=1, column=12, padx=2, pady=1)

    def recvMessage1FrameInit(self):
        self.recvMessage1Frame1 = Frame(self.tab1)
        self.recvMessage1Frame1.grid(row=2, column=0, columnspan=11)
        self.s1 = Scrollbar(self.recvMessage1Frame1, orient=VERTICAL)
        self.s1.grid(row=2, column=11, sticky="ns")
        self.listreadcan1 = Listbox(
            self.recvMessage1Frame1,
            font=("Arial", 12),
            height=20,
            width=90,
            yscrollcommand=self.s1.set,
        )
        self.listreadcan1.grid(row=2, column=0, columnspan=11, sticky="nw")
        self.s1.config(command=self.listreadcan1.yview)
        self.bt_clear_CAN1 = Button(
            self.recvMessage1Frame1,
            text="清空",
            command=self.clearcan1,
        )
        self.bt_clear_CAN1.grid(row=2, column=12, padx=2, pady=1)
        pass

    def can2TabInit(self):
        # 发送数据界面
        self.sendMessage2FrameInit()

        # 接收数据界面
        self.recvMessage2FrameInit()

    def sendMessage2FrameInit(self):
        self.lb_ID_CAN2 = Label(
            self.tab2, text="ID(Hex)", borderwidth=3, font=("Arial", 12)
        )
        self.lb_ID_CAN2.grid(row=0, column=0, sticky="w")
        self.e_ID_CAN2 = Entry(self.tab2, font=("Arial", 12))
        self.e_ID_CAN2.grid(row=1, column=0, sticky="w")
        self.e_ID_CAN2.insert(0, "00000001")
        self.ext_CAN2 = IntVar()
        self.cb_Ext_CAN2 = Checkbutton(
            self.tab2,
            text="Extended",
            variable=self.ext_CAN2,
        )
        self.cb_Ext_CAN2.grid(row=0, column=1, sticky="w")
        self.rtr_CAN2 = IntVar()
        self.cb_Rtr_CAN2 = Checkbutton(
            self.tab2,
            text="RTR",
            variable=self.rtr_CAN2,
        )
        self.cb_Rtr_CAN2.grid(row=1, column=1, sticky="w")
        self.ct_Length_CAN2 = tkinter.tix.Control(
            self.tab2, label="Length(0-8):", integer=True, max=8, min=0, value=8, step=1
        )
        self.ct_Length_CAN2.grid(row=0, column=7, columnspan=4, sticky="w")
        self.lb_Data_CAN2 = Label(
            self.tab2, text="Data(Hex)", borderwidth=3, font=("Arial", 12)
        )
        self.lb_Data_CAN2.grid(row=0, column=3, columnspan=4, sticky="w")
        self.Data0_CAN2 = StringVar()
        self.e_Data0_CAN2 = Entry(
            self.tab2,
            textvariable=self.Data0_CAN2,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data0_CAN2.grid(row=1, column=3, padx=2, pady=1, sticky="w")
        self.Data0_CAN2.set("00")
        self.Data1_CAN2 = StringVar()
        self.e_Data1_CAN2 = Entry(
            self.tab2,
            textvariable=self.Data1_CAN2,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data1_CAN2.grid(row=1, column=4, padx=2, pady=1, sticky="w")
        self.Data1_CAN2.set("01")
        self.Data2_CAN2 = StringVar()
        self.e_Data2_CAN2 = Entry(
            self.tab2,
            textvariable=self.Data2_CAN2,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data2_CAN2.grid(row=1, column=5, padx=2, pady=1, sticky="w")
        self.Data2_CAN2.set("02")
        self.Data3_CAN2 = StringVar()
        self.e_Data3_CAN2 = Entry(
            self.tab2,
            textvariable=self.Data3_CAN2,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data3_CAN2.grid(row=1, column=6, padx=2, pady=1, sticky="w")
        self.Data3_CAN2.set("03")
        self.Data4_CAN2 = StringVar()
        self.e_Data4_CAN2 = Entry(
            self.tab2,
            textvariable=self.Data4_CAN2,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data4_CAN2.grid(row=1, column=7, padx=2, pady=1, sticky="w")
        self.Data4_CAN2.set("04")
        self.Data5_CAN2 = StringVar()
        self.e_Data5_CAN2 = Entry(
            self.tab2,
            textvariable=self.Data5_CAN2,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data5_CAN2.grid(row=1, column=8, padx=2, pady=1, sticky="w")
        self.Data5_CAN2.set("05")
        self.Data6_CAN2 = StringVar()
        self.e_Data6_CAN2 = Entry(
            self.tab2,
            textvariable=self.Data6_CAN2,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data6_CAN2.grid(row=1, column=9, padx=2, pady=1, sticky="w")
        self.Data6_CAN2.set("06")
        self.Data7_CAN2 = StringVar()
        self.e_Data7_CAN2 = Entry(
            self.tab2,
            textvariable=self.Data7_CAN2,
            width=3,
            font=("Arial", 12),
        )
        self.e_Data7_CAN2.grid(row=1, column=10, padx=2, pady=1, sticky="w")
        self.Data7_CAN2.set("07")
        self.bt_send_CAN2 = Button(
            self.tab2,
            text="发送数据",
            state="disabled",
            command=self.sendcan2,
        )
        self.bt_send_CAN2.grid(row=1, column=12, padx=2, pady=1)

    def recvMessage2FrameInit(self):
        self.recvMessage1Frame2 = Frame(self.tab2)
        self.recvMessage1Frame2.grid(row=2, column=0, columnspan=11)
        self.s2 = Scrollbar(self.recvMessage1Frame2, orient=VERTICAL)
        self.s2.grid(row=2, column=11, sticky="ns")
        self.listreadcan2 = Listbox(
            self.recvMessage1Frame2,
            font=("Arial", 12),
            height=20,
            width=90,
            yscrollcommand=self.s2.set,
        )
        self.listreadcan2.grid(row=2, column=0, columnspan=11, sticky="nw")
        self.s2.config(command=self.listreadcan2.yview)
        self.bt_clear_CAN2 = Button(
            self.tab2,
            text="清空",
            command=self.clearcan2,
        )
        self.bt_clear_CAN2.grid(row=2, column=12, padx=2, pady=1)

    def tab5InitTestPanel(self):
        self.tab5Frame1 = Frame(self.tab5)
        self.tab5Frame1.grid(row=1, column=1)
        self.tab5Frame1_1 = Frame(self.tab5Frame1)
        self.tab5Frame1_1.grid(row=1, column=1)
        # TODO: frame1 预留位置

        self.tab5Frame1_2 = Frame(self.tab5Frame1)
        self.tab5Frame1_2.grid(row=2, column=1)
        self.setModelNameAndFunction()
        for PosId in self.MODELIDLIST:
            self.tab5btnUnitTest = Button(
                self.tab5Frame1_2,
                text=self.testmodelDict[PosId].name,
                command=self.testfuncDict[PosId],
            )
            # PosId 是一个行列的两位数字符串
            self.tab5btnUnitTest.grid(row=PosId[0], column=PosId[1], padx=10, pady=10)
        pass


    # 生产单元测试的窗口实例，然后测试完成自动隐藏窗口
    def unitTestCommand(self,posId):
        def wrapper():
            def inner():
                # 设定最大重测次数
                test_counts = 3
                for i in range(test_counts):
                    # testfuncDict里面放的是单元测试的构造函数，运行时候会生产单元测试的面板,frame00就是实例
                    # 后续要将frame00窗口隐藏，以提供更好的体验。
                    frame00 = self.testfuncDict[posId]()
                    dev_logger.debug('-----')

                    # TODO: frame00.allTest就是运行当前单元测试的批量测试， 也可以用btn的invoke模拟按钮被按下来实现(该方法暂未验证)
                    # 此处把allTest的return值拿到，用来在主界面显示
                    testResult = frame00.allTest()

                    # 拿到alltest的结果
                    resFlag,resString = testResult.result()

                    # 测试完成后隐藏窗口
                    frame00.withdraw()
                    
                    if resFlag:
                        break

                # userwindow中的信息展示
                self.userWindowFrame2_1TestResMsg[posId].configure(text=resString)
                foreground = 'green' if resFlag else 'red'
                self.userWindowFrame2_1TestResMsg[posId].configure(foreground=foreground)

                # 保留测试record信息
                recordStringLogger = f'VIN--{self.curVinCode.get():>17}--{frame00.name}:--{resString}' 
                record_logger.debug(recordStringLogger)

                # 增加当前时间
                recordStringListobx = f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}--{recordStringLogger}'
                # print('----',recordStr)
                self.userWindowFrame6recordListbox.insert('end', recordStringListobx)
                # 设置条目颜色
                if not resFlag:
                    self.userWindowFrame6recordListbox.itemconfig('end', background='pink')
                self.userWindowFrame6recordListbox.see(self.userWindowFrame6recordListbox.size())



            # FIXME: 此处用一个单独的线程来跑，否则result()函数会把主线程阻塞，导致无法正常工作
            self.pool.submit(inner)
            
        return wrapper




    @abstractmethod
    def setModelNameAndFunction(self):
        pass

    def clearcan1(self):
        self.listreadcan1.delete(0, END)

    def clearcan2(self):
        self.listreadcan2.delete(0, END)

    def caninit(self):
        self.musbcanopen, self.rec_CAN1, self.rec_CAN2
        if self.musbcanopen == False:
            initconfig = INIT_CONFIG()
            # 0x657 和 0x512 两个id的滤波设置
            # initconfig.acccode = 0x82400000  # 设置验收码
            # initconfig.accmask = 0x68Bfffff  # 设置屏蔽码


            # 0x657 一个id的滤波设置
            initconfig.acccode = 0xCAE00000  # 设置验收码
            initconfig.accmask = 0x001FFFFF  # 设置屏蔽码

            # initconfig.acccode = 0  # 设置验收码
            # initconfig.accmask = 0xffffffff  # 设置屏蔽码
            initconfig.filter = 1  # 设置滤波使能
            mbaudcan1 = self.baudvaluecan1.get()
            mbaudcan2 = self.baudvaluecan2.get()
            # 打开设备
            if self.ecan.OpenDevice(USBCAN2, DevIndex) != STATUS_OK:
                prod_logger.warning("Error, 请先打开设备")
                tkinter.messagebox.showinfo("ERROR", "OpenDevice Failed!")
                return
            initconfig.timing0, initconfig.timing1 = getTiming(mbaudcan1)
            # TODO: 正式使用时要把模式改为0
            initconfig.mode = 0
            # 初始化CAN1
            if self.ecan.InitCan(USBCAN2, DevIndex, Channel1, initconfig) != STATUS_OK:
                prod_logger.warning("Error, 请先打开设备")
                tkinter.messagebox.showinfo("ERROR", "InitCan CAN1 Failed!")
                self.ecan.CloseDevice(USBCAN2, DevIndex)
                return
            # 初始化CAN2
            initconfig.timing0, initconfig.timing1 = getTiming(mbaudcan2)
            if self.ecan.InitCan(USBCAN2, DevIndex, Channel2, initconfig) != STATUS_OK:
                prod_logger.warning("Error, 请先打开设备")
                tkinter.messagebox.showinfo("ERROR", "InitCan CAN2 Failed!")
                self.ecan.CloseDevice(USBCAN2, DevIndex)
                return
            if self.ecan.StartCan(USBCAN2, DevIndex, Channel1) != STATUS_OK:
                prod_logger.warning("Error, 请先打开设备")
                tkinter.messagebox.showinfo("ERROR", "StartCan CAN1 Failed!")
                self.ecan.CloseDevice(USBCAN2, DevIndex)
                return
            if self.ecan.StartCan(USBCAN2, DevIndex, Channel2) != STATUS_OK:
                prod_logger.warning("Error, 请先打开设备")
                tkinter.messagebox.showinfo("ERROR", "StartCan CAN2 Failed!")
                self.ecan.CloseDevice(USBCAN2, DevIndex)
                return
            
            # 连入总线时，首先清空缓存区的帧数据
            self.clearBuffer1()
            self.clearBuffer2()

            self.musbcanopen = True
            self.rec_CAN1 = 1
            self.rec_CAN2 = 1
            self.btopen.configure(text="关闭设备")
            self.btreadinfo.configure(state="normal")
            self.bt_send_CAN1.configure(state="normal")
            self.bt_send_CAN2.configure(state="normal")
        else:
            self.musbcanopen = False
            self.ecan.CloseDevice(USBCAN2, DevIndex)
            self.btopen.configure(text="打开设备")
            self.lbsn.configure(text="SN:")
            self.btreadinfo.configure(state="disabled")
            self.bt_send_CAN1.configure(state="disabled")
            self.bt_send_CAN2.configure(state="disabled")

    def clearBuffer1(self):
        # 清空缓存区的帧数据
        self.ecan.ClearBuffer(USBCAN2,DevIndex, Channel1)

    def clearBuffer2(self):
        # 清空缓存区的帧数据
        self.ecan.ClearBuffer(USBCAN2,DevIndex, Channel2)

    def readmess(self):
        self.musbcanopen
        if self.musbcanopen == False:
            prod_logger.warning("Error, 请先打开设备")
            tkinter.messagebox.showinfo("ERROR", "请先打开设备")
        else:
            mboardinfo, ret = self.ecan.ReadBoardInfo(
                USBCAN2, DevIndex
            )  # 读取设备信息需要在打开设备后执行
            if ret == STATUS_OK:
                mstr = ""
                for i in range(0, 10):
                    mstr = mstr + chr(
                        mboardinfo.str_Serial_Num[i]
                    )  # 结构体中str_Serial_Num内部存放存放SN号的ASC码
                self.lbsn.configure(text="SN:" + mstr)

            else:
                self.lbsn.configure(text="Read info Fault")

    def ReadCAN(self):
        self.musbcanopen, self.rec_CAN1, self.rec_CAN2
        while True:
            if self.musbcanopen == True and self.manualReadCanCounts == 0:
                len, rec, ret = self.ecan.Receivce(USBCAN2, DevIndex, Channel1, 30)
                # dev_logger.debug('len:'+ str(len) + '---ret:' + str(ret))
                # if len > 0 and ret == 1:
                if ret > 0:
                    for i in range(ret):
                        # dev_logger.debug(f'i:{i}')
                        mstr = "Rec: " + str(self.rec_CAN1)
                        self.rec_CAN1 = self.rec_CAN1 + 1
                        if rec[i].TimeFlag == 0:
                            mstr = mstr + " Time: "
                        else:
                            mstr = mstr + " Time:" + hex(rec[i].TimeStamp).zfill(8)
                        if rec[i].ExternFlag == 0:
                            thisId = hex(rec[i].ID).zfill(3)
                            mstr = mstr + " ID:" + thisId + " Format:Stand "
                        else:
                            thisId = hex(rec[i].ID).zfill(8)
                            mstr = mstr + " ID:" + thisId + " Format:Exten "
                        if rec[i].RemoteFlag == 0:
                            mstr = mstr + " Type:Data " + " Data: "
                            thisData = ""
                            for j in range(0, rec[i].DataLen):
                                # mstr = mstr + hex(rec[i].data[i]).zfill(2) + " "
                                curbyte = hex(rec[i].data[j]).zfill(2)
                                mstr = mstr + curbyte + " "
                                thisData += curbyte

                            # 把收到数据加工成8字节的16进制表示
                            dataList = thisData.split("0x")
                            recvData = "".join([x.zfill(2) for x in dataList[1:]])
                            # dev_logger.debug(f"收到ID:{thisId}--数据:{recvData}")
                            thisMsg = RecvMsg(thisId, recvData)
                            # 往定长队列中添加信息
                            dev_logger.debug(thisMsg)
                            with self.recvMsgDequeLock:
                            # if 1:
                                # if thisMsg.id in ['0x5d3','0x657','0x280','0x570','0x512', '0x513', '0x514']:
                                if 1:
                                    self.recvMsgDeque.append(thisMsg)
                                    # dev_logger.debug(thisMsg)
                                    # dev_logger.debug(self.recvMsgDeque)
                        else:
                            mstr = mstr + " Type:Romte " + " Data: Remote Request"

                        if self.listreadcan1.size() > 1000:
                            self.listreadcan1.delete(0, END)
                        self.listreadcan1.insert("end", mstr)
                        self.listreadcan1.see(self.listreadcan1.size())

                len2, rec2, ret2 = self.ecan.Receivce(USBCAN2, DevIndex, Channel2, 1)
                if len2 > 0 and ret2 == 1:
                    mstr = "Rec: " + str(self.rec_CAN2)
                    self.rec_CAN2 = self.rec_CAN2 + 1
                    if rec2[0].TimeFlag == 0:
                        mstr = mstr + " Time: "
                    else:
                        mstr = mstr + " Time:" + hex(rec2[0].TimeStamp).zfill(8)
                    if rec2[0].ExternFlag == 0:
                        mstr = (
                            mstr + " ID:" + hex(rec2[0].ID).zfill(3) + " Format:Stand "
                        )
                    else:
                        mstr = (
                            mstr + " ID:" + hex(rec2[0].ID).zfill(8) + " Format:Exten "
                        )
                    if rec2[0].RemoteFlag == 0:
                        mstr = mstr + " Type:Data " + " Data: "
                        for i in range(0, rec2[0].DataLen):
                            mstr = mstr + hex(rec2[0].data[i]).zfill(2) + " "
                    else:
                        mstr = mstr + " Type:Romte " + " Data: Remote Request"

                    if self.listreadcan2.size() > 1000:
                        self.listreadcan2.delete(0, END)
                    self.listreadcan2.insert("end", mstr)
                    self.listreadcan2.see(self.listreadcan2.size())

            time.sleep(0.001)

    def sendcan1(self):
        self.musbcanopen
        if self.musbcanopen == False:
            prod_logger.warning("Error, 请先打开设备")
            tkinter.messagebox.showinfo("ERROR", "请先打开设备")
        else:
            canobj = CAN_OBJ()
            canobj.ID = int(self.e_ID_CAN1.get(), 16)
            canobj.DataLen = int(self.ct_Length_CAN1["value"])

            # 将输入的字符串,转换为int类型,第二个参数表示进制选择.
            canobj.data[0] = int(self.e_Data0_CAN1.get(), 16)
            canobj.data[1] = int(self.e_Data1_CAN1.get(), 16)
            canobj.data[2] = int(self.e_Data2_CAN1.get(), 16)
            canobj.data[3] = int(self.e_Data3_CAN1.get(), 16)
            canobj.data[4] = int(self.e_Data4_CAN1.get(), 16)
            canobj.data[5] = int(self.e_Data5_CAN1.get(), 16)
            canobj.data[6] = int(self.e_Data6_CAN1.get(), 16)
            canobj.data[7] = int(self.e_Data7_CAN1.get(), 16)

            # if canobj.ID == 0x5d3:
                # dev_logger.debug(f'0x5d3 send msg.......{hex(canobj.data[4]).zfill(4)}')
            # 两个Flag默认为零
            canobj.RemoteFlag = self.rtr_CAN1.get()
            canobj.ExternFlag = self.ext_CAN1.get()
            for i in range(3):
                self.ecan.Tramsmit(USBCAN2, DevIndex, Channel1, canobj)

    def sendcan2(self):
        self.musbcanopen
        if self.musbcanopen == False:
            prod_logger.warning("Error, 请先打开设备")
            tkinter.messagebox.showinfo("ERROR", "请先打开设备")
        else:
            canobj = CAN_OBJ()
            canobj.ID = int(self.e_ID_CAN2.get(), 16)
            canobj.DataLen = int(self.ct_Length_CAN2["value"])
            canobj.data[0] = int(self.e_Data0_CAN2.get(), 16)
            canobj.data[1] = int(self.e_Data1_CAN2.get(), 16)
            canobj.data[2] = int(self.e_Data2_CAN2.get(), 16)
            canobj.data[3] = int(self.e_Data3_CAN2.get(), 16)
            canobj.data[4] = int(self.e_Data4_CAN2.get(), 16)
            canobj.data[5] = int(self.e_Data5_CAN2.get(), 16)
            canobj.data[6] = int(self.e_Data6_CAN2.get(), 16)
            canobj.data[7] = int(self.e_Data7_CAN2.get(), 16)
            canobj.RemoteFlag = self.rtr_CAN2.get()
            canobj.ExternFlag = self.ext_CAN2.get()
            self.ecan.Tramsmit(USBCAN2, DevIndex, Channel2, canobj)

    def sendcan1_manual(
        self, ID=0x5D3, data_string=None, DataLen=8, RemoteFlag=0, ExternFlag=0
    ):
        if data_string is None:
            data = ["0", "0", "0", "0", "0", "0", "0", "0"]
        else:
            data = []
            for i in range(8):
                data.append(data_string[i * 2 : i * 2 + 2])
        

        if self.musbcanopen == False:
            prod_logger.warning("Error, 请先打开设备")
            tkinter.messagebox.showinfo("ERROR", "请先打开设备")
            return "error"
        else:
            canobj = CAN_OBJ()
            canobj.ID = int(ID)
            canobj.DataLen = int(DataLen)
            canobj.data[0] = int(data[0], 16)
            canobj.data[1] = int(data[1], 16)
            canobj.data[2] = int(data[2], 16)
            canobj.data[3] = int(data[3], 16)
            canobj.data[4] = int(data[4], 16)
            canobj.data[5] = int(data[5], 16)
            canobj.data[6] = int(data[6], 16)
            canobj.data[7] = int(data[7], 16)
            canobj.RemoteFlag = RemoteFlag
            canobj.ExternFlag = ExternFlag
            res = self.ecan.Tramsmit(USBCAN2, DevIndex, Channel1, canobj)
            # dev_logger.debug(f'{type(res)}res:{res}')
            return res


class GcanView(GcanViewBase):
    def setModelNameAndFunction(self):
        with open("unittestConfig.json", "r", encoding="utf-8") as fp:
            self.configJson = json.loads(fp.read())

        dev_logger.debug(self.configJson)
        for item in self.configJson.get("data"):
            # 每个模块的测试配置数据
            cur = TestDataStucture(**item)
            self.TESTCONFIGDATALIST.append(cur)

        for model in self.TESTCONFIGDATALIST:
            self.testmodelDict[model.PosId] = model

        # self.TESTFUNCLIST["00"] = self.testR0C0
        # self.TESTFUNCLIST["01"] = self.testR0C1

        # 先生产统一的函数,存在列表中,然后有个别特殊情况的,可以再后面更新函数
        self.generateTestfuncList()
        # 个别特殊需求的测试需求可以单独在这里定义覆盖掉之前的统一定义
        self.testfuncListupdate()

    def testfuncListupdate(self):
        """
        针对特殊需求的测试模块单独设定测试功能
        """
        self.testmodelDict["76"] = TestDataStucture(
            PosId="76",
            MsgId=0x280,
            name="发出发动机信号",
            testDatas=[
                UnitTestDataStructure("发动机信号", "0000000000000000", "0000000000000000")
            ],
        )

        self.testfuncDict["76"] = self.testR7C6

        self.testmodelDict["77"] = TestDataStucture(
            PosId="77",
            MsgId=0x570,
            name="整车上电信号",
            testDatas=[
                UnitTestDataStructure("整车上电", "0000000000000000", "0000000000000000")
            ],
        )

        self.testfuncDict["77"] = self.testR7C7
        pass

    def createUnitTestFramewrapper(self, posid):
        def inner():
            PosId = posid
            name = self.testmodelDict[posid].name
            MsgId = self.testmodelDict[posid].MsgId
            testDatas = self.testmodelDict[posid].testDatas
            rcevId = self.testmodelDict[posid].rcevId
            curToplevel = FunctestUnit(
                self,rcevId=rcevId, PosId=PosId, name=name, MsgId=MsgId, testDatas=testDatas
            )
            
            # 设置窗口置顶，确保设置的窗口在主窗口上面
            curToplevel.attributes('-topmost', True)
            return curToplevel

        return inner

    def generateTestfuncList(self):
        # 创建一个生成闭包的函数,针对每个不同的PosId生产闭包
        # def wrapper(posid):
        #     def inner():
        #         PosId = posid
        #         name = self.testmodelDict[posid].name
        #         MsgId = self.testmodelDict[posid].MsgId
        #         testDatas = self.testmodelDict[posid].testDatas
        #         rcevId = self.testmodelDict[posid].rcevId
        #         curToplevel = FunctestUnit(
        #             self,rcevId=rcevId, PosId=PosId, name=name, MsgId=MsgId, testDatas=testDatas
        #         )
                

        #         # 设置窗口置顶，确保设置的窗口在主窗口上面
        #         curToplevel.attributes('-topmost', True)
        #         return curToplevel

        #     return inner

        for posid in self.testmodelDict:
            self.testfuncDict[posid] = self.createUnitTestFramewrapper(posid)

    def testR7C6(self):
        modelR7C6 = FunctestR7C6(self, PosId="76", name="发动机信号模拟")
        modelR7C6.withdraw()
        modelR7C6.sendDataBtnDict[0].invoke()

    def testR7C7(self):
        modelR7C7 = FunctestR7C7(self, PosId="77", name="整车上电信号模拟")
        modelR7C7.withdraw()
        modelR7C7.sendDataBtnDict[0].invoke()


def main():
    root = GcanView()
    root.mainloop()


if __name__ == "__main__":
    main()
